﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2024 LiLong                                                   **
**  This file is part of GnssLogs.                                              **
**                                                                              **
**  GnssLogs is free software: you can redistribute it and/or modify            **
**  it under the terms of the GNU Lesser General Public License as published by **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  GnssLogs is distributed in the hope that it will be useful,                 **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU Lesser General Public License for more details.                         **
**                                                                              **
**  You should have received a copy of the GNU Lesser General Public License    **
**  along with GnssLogs.  If not, see <https://www.gnu.org/licenses/>.          **
**********************************************************************************/
#pragma once

#include "../GPSEPHEM.h"
#include "LogHeaderDecoder.h"

namespace GnssLogs
{
namespace GPSEPHEMDecoder
{
using Sync   = TAO_PEGTL_STRING("#GPSEPHEMA");
using Header = LogHeaderDecoder::SyncExpression<Sync>;
// clang-format off
struct PRN : star<digit> {};
struct Tow : Floating {};
struct Health : star<digit> {};
struct IODE1 : star<digit> {};
struct IODE2 : star<digit> {};
struct Week : star<digit> {};
struct ZWeek : star<digit> {};
struct Toe : Floating {};
struct SemiMajorAxis : Floating {};
struct MeanMotionDifference : Floating {};
struct MeanAnomalyOfReferenceTime : Floating {};
struct Ecc : Floating {};
struct ArgumentOfPerigee : Floating {};
struct Cuc : Floating {};
struct Cus : Floating {};
struct Crc : Floating {};
struct Crs : Floating {};
struct Cic : Floating {};
struct Cis : Floating {};
struct InclinationAngleAtReferenceTime : Floating {};
struct RateOfInclinationAngle : Floating {};
struct LongitudeOfAscendingNodeOfOrbitPlane : Floating {};
struct RateOfRightAscension : Floating {};
struct IODC : star<digit> {};
struct TOC : Floating {};
struct TGD : Floating {};
struct AF0 : Floating {};
struct AF1 : Floating {};
struct AF2 : Floating {};
struct AS : sor<TAO_PEGTL_STRING("TRUE"), TAO_PEGTL_STRING("FALSE")> {};
struct CorrectedMeanMotion : Floating {};
struct URA : Floating {};
// clang-format on

using Expression = seq<Header,
                       PRN,
                       Split,
                       Tow,
                       Split,
                       Health,
                       Split,
                       IODE1,
                       Split,
                       IODE2,
                       Split,
                       Week,
                       Split,
                       ZWeek,
                       Split,
                       Toe,
                       Split,
                       SemiMajorAxis,
                       Split,
                       MeanMotionDifference,
                       Split,
                       MeanAnomalyOfReferenceTime,
                       Split,
                       Ecc,
                       Split,
                       ArgumentOfPerigee,
                       Split,
                       Cuc,
                       Split,
                       Cus,
                       Split,
                       Crc,
                       Split,
                       Crs,
                       Split,
                       Cic,
                       Split,
                       Cis,
                       Split,
                       InclinationAngleAtReferenceTime,
                       Split,
                       RateOfInclinationAngle,
                       Split,
                       LongitudeOfAscendingNodeOfOrbitPlane,
                       Split,
                       RateOfRightAscension,
                       Split,
                       IODC,
                       Split,
                       TOC,
                       Split,
                       TGD,
                       Split,
                       AF0,
                       Split,
                       AF1,
                       Split,
                       AF2,
                       Split,
                       AS,
                       Split,
                       CorrectedMeanMotion,
                       Split,
                       URA,
                       one<'*'>,
                       CRC32,
                       Tail>;

template<typename T>
struct Action
{
};

template<>
struct Action<Header> : LogHeaderDecoder::LogHeaderHelperNoSetValue
{
};

template<>
struct Action<PRN> : IntHelper<Action<PRN>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.prn = value; }
};

template<>
struct Action<Tow> : DoubleHelper<Action<Tow>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.tow = value; }
};

template<>
struct Action<Health> : IntHelper<Action<Health>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.health = value; }
};

template<>
struct Action<IODE1> : IntHelper<Action<IODE1>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.iode1 = value; }
};

template<>
struct Action<IODE2> : IntHelper<Action<IODE2>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.iode2 = value; }
};

template<>
struct Action<Week> : IntHelper<Action<Week>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.week = value; }
};

template<>
struct Action<ZWeek> : IntHelper<Action<ZWeek>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.zWeek = value; }
};

template<>
struct Action<Toe> : DoubleHelper<Action<Toe>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.toe = value; }
};

template<>
struct Action<SemiMajorAxis> : DoubleHelper<Action<SemiMajorAxis>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.semiMajorAxis = value; }
};

template<>
struct Action<MeanMotionDifference> : DoubleHelper<Action<MeanMotionDifference>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.meanMotionDifference = value; }
};

template<>
struct Action<MeanAnomalyOfReferenceTime> : DoubleHelper<Action<MeanAnomalyOfReferenceTime>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.meanAnomalyOfReferenceTime = value; }
};

template<>
struct Action<Ecc> : DoubleHelper<Action<Ecc>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.ecc = value; }
};

template<>
struct Action<ArgumentOfPerigee> : DoubleHelper<Action<ArgumentOfPerigee>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.argumentOfPerigee = value; }
};

template<>
struct Action<Cuc> : DoubleHelper<Action<Cuc>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.cuc = value; }
};

template<>
struct Action<Cus> : DoubleHelper<Action<Cus>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.cus = value; }
};

template<>
struct Action<Crc> : DoubleHelper<Action<Crc>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.crc = value; }
};

template<>
struct Action<Crs> : DoubleHelper<Action<Crs>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.crs = value; }
};

template<>
struct Action<Cic> : DoubleHelper<Action<Cic>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.cic = value; }
};

template<>
struct Action<Cis> : DoubleHelper<Action<Cis>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.cis = value; }
};

template<>
struct Action<InclinationAngleAtReferenceTime> : DoubleHelper<Action<InclinationAngleAtReferenceTime>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.inclinationAngleAtReferenceTime = value; }
};

template<>
struct Action<RateOfInclinationAngle> : DoubleHelper<Action<RateOfInclinationAngle>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.rateOfInclinationAngle = value; }
};

template<>
struct Action<LongitudeOfAscendingNodeOfOrbitPlane> : DoubleHelper<Action<LongitudeOfAscendingNodeOfOrbitPlane>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.longitudeOfAscendingNodeOfOrbitPlane = value; }
};

template<>
struct Action<RateOfRightAscension> : DoubleHelper<Action<RateOfRightAscension>>
{
    // clang-format off
    static void setValue(GPSEPHEM::Data& data, double value)
    {
        data.rateOfRightAscension = value;
    }
};

template<>
struct Action<IODC> : IntHelper<Action<IODC>, uint32_t>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.iodc = value; }
};

template<>
struct Action<TOC> : DoubleHelper<Action<TOC>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.toc = value; }
};

template<>
struct Action<TGD> : DoubleHelper<Action<TGD>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.tgd = value; }
};

template<>
struct Action<AF0> : DoubleHelper<Action<AF0>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.af0 = value; }
};

template<>
struct Action<AF1> : DoubleHelper<Action<AF1>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.af1 = value; }
};

template<>
struct Action<AF2> : DoubleHelper<Action<AF2>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.af2 = value; }
};

template<>
struct Action<AS>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GPSEPHEM::Data& data)
    {
        std::string str(in.begin(), in.size());
        if (str == "TRUE")
            data.as = true;
        else if (str == "FALSE")
            data.as = false;
        else
            return false;
        return true;
    }
};

template<>
struct Action<CorrectedMeanMotion> : DoubleHelper<Action<CorrectedMeanMotion>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.correctedMeanMotion = value; }
};

template<>
struct Action<URA> : DoubleHelper<Action<URA>>
{
    static void setValue(GPSEPHEM::Data& data, double value) { data.ura = value; }
};

template<>
struct Action<CRC32> : CRC32Helper<Action<CRC32>>
{
    static void setValue(GPSEPHEM::Data& data, uint32_t value) { data.crc32 = value; }
};
} // namespace GPSEPHEMDecoder
} // namespace GnssLogs